home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / libg_261.zip / libg_261 / libg++ / etc / fib / fib.cc < prev    next >
C/C++ Source or Header  |  1994-05-13  |  14KB  |  616 lines

  1. #include <stream.h>
  2. #include <Integer.h>
  3. #include <builtin.h>
  4.  
  5. // Fun and games with the Fibonacci function.
  6. // Also demonstrates use of the `named return value' extension.
  7.  
  8. // fib1 and fib2 from Doug Lea, others from Doug Schmidt.
  9.  
  10. /**********************************************************************/
  11.  
  12. // Standard iterative version:
  13.  
  14. // Schmidt uses convention that fib(0) = 1, not fib(0) = 0;
  15. // so I just increment n here.
  16.  
  17. Integer 
  18. fib1(int n)
  19. {
  20.   ++n;
  21.   if (n <= 0)
  22.     return 0;
  23.   else
  24.     {
  25.       Integer f = 1;
  26.       Integer prev = 0;
  27.  
  28.       while (n > 1)
  29.         {
  30.           Integer tmp = f;
  31.           f += prev;
  32.           prev = tmp;
  33.           --n;
  34.         }
  35.  
  36.       return f;
  37.     }
  38. }
  39.  
  40. /**********************************************************************/
  41.  
  42. //                                                       n
  43. // via transformed matrix multiplication  of     ( 0  1 )
  44. //                                               ( 1  1 )
  45. // simplified knowing that the matrix is always of 
  46. // the form  (a b)
  47. //           (b c)
  48. // (where (a, b) and (b, c) are conseq pairs of conseq fib's;
  49. // further simplified by realizing that c = a+b, so c is only
  50. // calculated implicitly.
  51. // Given all this, do the power via the standard russian
  52. // peasant divide-and-conquer algorithm, with
  53. // the current multiplier held in (p q)
  54. //                                (q -)
  55. //
  56. // This example also shows that using procedure calls instead of operators
  57. // can be faster for Integers
  58.  
  59.  
  60. // operator version
  61.  
  62. Integer 
  63. fib2(int n) 
  64. {
  65.   ++n;                          // 1-based for compatability; see above
  66.   Integer a = 0;
  67.   if (n <= 0)
  68.     return a;
  69.   else
  70.   {
  71.     Integer b = 1;
  72.     Integer p = 0;
  73.     Integer q = 1;
  74.     
  75.     for(;;)
  76.     {
  77.       if (n == 1)
  78.         return a * p + b * q;
  79.       else if (odd (n))
  80.       {
  81.         Integer aq = a * q;
  82.         Integer bq = b * q;
  83.         a = a * p + bq;
  84.         b = b * p  + aq + bq;
  85.       }
  86.       Integer qq = q * q;
  87.       q = (q * p) * 2L + qq;
  88.       p = (p * p) + qq;
  89.       n >>= 1;
  90.     }
  91.   }
  92. }
  93.  
  94. // with all expressions named, including return value
  95.  
  96. Integer 
  97. fib2a(int n)
  98. {
  99.   Integer a = 0;
  100.   ++n;
  101.   if (n <= 0)
  102.     return a;
  103.   else
  104.   {
  105.     Integer b = 1;
  106.     Integer p = 0;
  107.     Integer q = 1;
  108.     
  109.     for(;;)
  110.     {
  111.       if (n == 1)
  112.       {
  113.         Integer bq = b * q;
  114.         a *= p ;
  115.         a += bq;
  116.     return a;
  117.       }
  118.       else if (odd (n))
  119.       {
  120.         Integer aq = a * q;
  121.         Integer bq = b * q;
  122.         a *= p;
  123.         a += bq;
  124.         b *= p;
  125.         b += bq;
  126.         b += aq;
  127.       }
  128.       Integer qq = q * q;
  129.       Integer pp = p * p;
  130.       Integer pq = p * q;
  131.       q = pq;
  132.       q += pq;
  133.       q += qq;
  134.       p = pp;
  135.       p += qq;
  136.       n >>= 1;
  137.     }
  138.   }
  139. }
  140.  
  141. // procedure call version
  142.  
  143. Integer 
  144. fib2b(int n)
  145. {
  146.   Integer a = 0;
  147.   ++n; 
  148.  
  149.   if (n <= 0)
  150.     return a;
  151.   else
  152.   {
  153.     Integer b = 1;
  154.     Integer p = 0;
  155.     Integer q = 1;
  156.     
  157.     for(;;)
  158.     {
  159.       if (n == 1)
  160.       {
  161.         Integer bq; mul(b, q, bq);
  162.         mul(a, p, a);
  163.         add(a, bq, a);
  164.     return a;
  165.       }
  166.       else if (odd (n))
  167.       {
  168.         Integer aq; mul(a, q, aq);
  169.         Integer bq; mul(b, q, bq);
  170.         mul(a, p, a);
  171.         mul(b, p, b);
  172.         add(a, bq, a);
  173.         add(b, bq, b);
  174.         add(b, aq, b);
  175.       }
  176.       Integer qq; mul(q, q, qq);
  177.       Integer pp; mul(p, p, pp);
  178.       Integer pq; mul(p, q, pq);
  179.       add(pq, pq, q);
  180.       add(q, qq, q);
  181.       add(pp, qq, p);
  182.       n >>= 1;
  183.     }
  184.   }
  185. }
  186.  
  187. // procedure call version, reusing variables
  188.  
  189. Integer 
  190. fib2c(int n)
  191. {
  192.   Integer a = 0;
  193.   ++n; 
  194.  
  195.   if (n <= 0)
  196.     return a;
  197.   else
  198.   {
  199.     Integer b = 1;
  200.     Integer p = 0;
  201.     Integer q = 1;
  202.     Integer bq, aq, qq, pp, pq;
  203.     for(;;)
  204.     {
  205.       if (n == 1)
  206.       {
  207.         mul(b, q, bq);
  208.         mul(a, p, a);
  209.         add(a, bq, a);
  210.     return a;
  211.       }
  212.       else if (odd (n))
  213.       {
  214.         mul(a, q, aq);
  215.         mul(b, q, bq);
  216.         mul(a, p, a);
  217.         mul(b, p, b);
  218.         add(a, bq, a);
  219.         add(b, bq, b);
  220.         add(b, aq, b);
  221.       }
  222.       mul(q, q, qq);
  223.       mul(p, p, pp);
  224.       mul(p, q, pq);
  225.       add(pq, pq, q);
  226.       add(q, qq, q);
  227.       add(pp, qq, p);
  228.       n >>= 1;
  229.     }
  230.   }
  231. }
  232.  
  233. /**********************************************************************/
  234. // Ullman memoizers:
  235.  
  236. class Ullman_Array
  237. {
  238.   // Ullman's array implementation allows Initialization, Store, and Fetch
  239.   // in O(1) time.  Although it takes O(n) space the time to initialize enables
  240.   // us to compute a Fibonacci number in O(log n) time!
  241.   // The basic concept of Ullman's array implementation is the use of a 
  242.   // Hand_Shake_Array and an Index_Array.  An Index location in the array is 
  243.   // only considered initialized if the contents in the Hand_Shake_Array and
  244.   // Index_Array point to each other, i.e. they ``shake hands!''
  245.   
  246. private:
  247.   int       max_array_range;
  248.   int       max_set_size;
  249.   int      *index_array;
  250.   int      *hand_shake_array;
  251.   Integer  *storage_array;
  252.   int       current_max;
  253.   
  254. public:
  255.   Integer uninitialized;
  256.  
  257.   Ullman_Array (int array_range, int set_size);
  258.   void    store (int index, const Integer &item);
  259.   Integer fetch (int index);
  260.  
  261. };
  262.  
  263. inline
  264. Ullman_Array::Ullman_Array (int array_range, int set_size)
  265. {
  266.   max_array_range  = array_range;
  267.   max_set_size     = set_size;
  268.   index_array      = new int[max_array_range + 1];
  269.   hand_shake_array = new int[max_set_size];
  270.   storage_array    = new Integer[max_set_size];
  271.   current_max      = -1;
  272.   uninitialized    = 0; // No fibonacci number has value of 0.
  273. }
  274.  
  275. // Store Item at the proper Index of the Ullman array.
  276. // The Hand_Shake_Array determines whether the Index has already
  277. // been stored into.  If it has NOT, then a new location for it
  278. // is set up in the Storage_Array and the Hand_Shake_Array and Index_Array 
  279. // are set to point at each other.
  280.  
  281. inline void
  282. Ullman_Array::store (int index, const Integer &item)
  283. {
  284.   int  hand_shake_index = index_array[index];
  285.   
  286.   if (hand_shake_index > current_max || hand_shake_index < 0
  287.       || index != hand_shake_array[hand_shake_index])
  288.     {
  289.       hand_shake_index                   = ++current_max;
  290.       hand_shake_array[hand_shake_index] = index;
  291.       index_array[index]                 = hand_shake_index;
  292.     }
  293.   storage_array[hand_shake_index] = item;
  294. }
  295.  
  296. // Returns uninitialized if not initialized, else returns Item at Index.
  297.  
  298. inline Integer
  299. Ullman_Array::fetch(int index) 
  300. {
  301.   int hand_shake_index = index_array[index];
  302.   
  303.   if (hand_shake_index > current_max || hand_shake_index < 0
  304.       || index != hand_shake_array[hand_shake_index])
  305.     return uninitialized;
  306.   else 
  307.     return storage_array[hand_shake_index];
  308. }
  309.  
  310. /**********************************************************************/
  311.  
  312. class Memo_Fib : public Ullman_Array
  313. {
  314. public:
  315.   Memo_Fib (int fib_num);
  316.   Integer fib3 (int n);
  317.   Integer fib3a (int n);
  318. };
  319.  
  320.  
  321. // The total number of Items computed by the Fib routine is bounded by
  322. // 4 * ceiling of log base 2 of Fib_Num.  Of course, the basis of the
  323. // recurrence is Fib(0) == 1 and Fib(1) == 1!
  324.  
  325. Memo_Fib::Memo_Fib (int fib_num) : Ullman_Array(fib_num, (4 * lg (fib_num)))
  326. {
  327.   store (0, 1);
  328.   store (1, 1);
  329. }
  330.  
  331. // Uses the memoization technique to reduce the time complexity to calculate
  332. // the nth Fibonacci number in O(log n) time.  If the value of ``n'' is
  333. // already in the table we return it in O(1) time.  Otherwise, we use the
  334. // super-nifty divide-and-conquer algorithm to break the problem up into
  335. // 4 pieces of roughly size n/2 and solve them recursively.  Although this
  336. // looks like an O(n^2) recurrence the memoization reduces the number of
  337. // recursive calls to O(log n)!
  338.  
  339. Integer
  340. Memo_Fib::fib3 (int n)
  341. {
  342.   Integer fib = fetch(n);
  343.   if (fib == uninitialized)
  344.     {
  345.       int m = n >> 1;
  346.       
  347.       fib = fib3 (m) * fib3 (n - m) + fib3 (m - 1) * fib3 (n - m - 1);
  348.       store (n, fib);
  349.     }   
  350.   return fib;
  351. }
  352.  
  353. // The same, with procedure calls instead of operators
  354.  
  355. Integer
  356. Memo_Fib::fib3a (int n)
  357. {
  358.   Integer fib = fetch(n);
  359.   if (fib == uninitialized)
  360.     {
  361.       int m = n >> 1;
  362.       Integer tmp;
  363.       mul(fib3a(m), fib3a(n - m), fib);
  364.       mul(fib3a(m - 1), fib3a(n - m - 1), tmp);
  365.       add(fib, tmp, fib);
  366.       store (n, fib);
  367.     }   
  368.   return fib;
  369. }
  370.  
  371. /**********************************************************************/
  372.  
  373. // Here's a linear-time dynamic programming solution to the same problem. 
  374. // It makes use of G++/GCC dynamic arrays to build a table of ``n'' partial 
  375. // solutions.  Naturally, there is an O(1) space solution, but this O(n) 
  376. // approach is somewhat more intuitive and follows the ``original'' recurrence
  377. // more closely.
  378.  
  379. Integer 
  380. fib4 (int n)
  381. {
  382. #if defined (__GNUC__) && !defined (__STRICT_ANSI__)
  383.   Integer table[n + 1];
  384. #else
  385.   Integer *table = new Integer[n + 1];
  386. #endif
  387.   table[0] = 1;
  388.   table[1] = 1;
  389.  
  390.   for (int i = 2; i <= n; i++) 
  391.     table[i] = table[i - 1] + table[i - 2];
  392.   
  393. #ifdef __GNUC__
  394.   return table[n];
  395. #else
  396.   Integer tmp = table[n];
  397.   delete [] table;
  398.   return tmp;
  399. #endif
  400. }
  401.  
  402. /**********************************************************************/
  403.  
  404. // The extended integers provide numbers of the form:
  405. // (base+sqrt(5)*Rad_5)/2^Pow_2
  406. // These are used to find the solution to the closed form of fib(n). 
  407.  
  408. struct Extended_Int
  409. {
  410.   Integer base;
  411.   Integer rad_5;
  412.   int     pow_2;
  413.  
  414.   Extended_Int (const Integer ¶m1, const Integer ¶m2, int param3);
  415.   Extended_Int (const Extended_Int& param);
  416.   Extended_Int (void) {}
  417.  
  418.   friend inline Extended_Int operator- (const Extended_Int ¶m1,
  419.                     const Extended_Int ¶m2);
  420.   friend Extended_Int operator* (const Extended_Int ¶m1,
  421.                  const Extended_Int ¶m2);
  422.   friend inline Extended_Int sqrt (const Extended_Int ¶m1);
  423.   friend Extended_Int pow  (const Extended_Int ¶m1, int num);
  424. };
  425.  
  426. inline
  427. Extended_Int::Extended_Int (const Integer ¶m1, const Integer ¶m2,
  428.                 int param3)
  429. {
  430.   base  = param1;
  431.   rad_5 = param2;
  432.   pow_2 = param3;
  433. }
  434.  
  435. inline
  436. Extended_Int::Extended_Int (const Extended_Int ¶m)
  437. {
  438.   base  = param.base;
  439.   rad_5 = param.rad_5;
  440.   pow_2 = param.pow_2;
  441. }
  442.  
  443. inline Extended_Int 
  444. operator- (const Extended_Int ¶m1, const Extended_Int ¶m2)
  445. {
  446.   Extended_Int temp;
  447.   temp.base  = param1.base - param2.base;
  448.   temp.rad_5 = param1.rad_5 - param2.rad_5;
  449.   temp.pow_2 = param1.pow_2;
  450.   return temp;
  451. }
  452.  
  453. Extended_Int 
  454. operator* (const Extended_Int ¶m1, const Extended_Int ¶m2)
  455. {
  456.   Extended_Int temp;
  457.   temp.base  = param1.base * param2.base + 5L * param1.rad_5 * param2.rad_5;
  458.   temp.rad_5 = param1.base * param2.rad_5 + param1.rad_5 * param2.base;
  459.   temp.pow_2 = param1.pow_2 + param2.pow_2;
  460.  
  461.   while (temp.pow_2 > 0 && !(odd (temp.base) || odd (temp.rad_5)))
  462.     {
  463.       temp.base >>= 1;
  464.       temp.rad_5 >>= 1;
  465.       temp.pow_2--;
  466.     }
  467.  
  468.   return temp;
  469. }
  470.  
  471. inline Extended_Int 
  472. sqrt (const Extended_Int ¶m1)
  473. {
  474.   return param1 * param1;
  475. }
  476.  
  477. Extended_Int 
  478. pow (const Extended_Int ¶m1, int num) 
  479. {
  480.   if (num > 1)
  481.     return odd (num)
  482.       ? param1 * sqrt (pow (param1, num >> 1)) : sqrt (pow (param1, num >> 1));
  483.   else 
  484.     return param1;
  485. }
  486.  
  487. /**********************************************************************/
  488.  
  489. // Calculates fib (n) by solving the closed form of the recurrence. 
  490.  
  491. class Closed_Form : private Extended_Int
  492. {
  493. private:
  494.   Extended_Int cons1;
  495.   Extended_Int cons2;
  496.  
  497. public:
  498.   Closed_Form (void): cons1 (1, 1, 1), cons2 (1, -1, 1) {}
  499.   Integer fib5 (int n);
  500. };
  501.  
  502. Integer
  503. Closed_Form::fib5 (int num) 
  504. {
  505.   Extended_Int temp = pow (cons1, num + 1) - pow (cons2, num + 1);
  506.   
  507.   while (temp.pow_2 > 0 && !(odd (temp.base) || odd (temp.rad_5)))
  508.     {
  509.       temp.base  >>= 1;
  510.       temp.rad_5 >>= 1;
  511.       temp.pow_2--;
  512.     }
  513.   
  514.   return temp.rad_5;
  515. }
  516.  
  517. /**********************************************************************/
  518.  
  519. static const int DEFAULT_SIZE = 10000;
  520.  
  521.  
  522. void dofib1(int fib_num)
  523. {
  524.   start_timer ();
  525.   Integer result = fib1 (fib_num);
  526.   double time = return_elapsed_time(0.0);
  527.   cout << "fib1 = " << result << ". Time = " << time << "\n";
  528. }
  529.  
  530. void dofib2(int fib_num)
  531. {
  532.   start_timer ();
  533.   Integer result = fib2 (fib_num);
  534.   double time = return_elapsed_time(0.0);
  535.   cout << "fib2 = " << result << ". Time = " << time << "\n";
  536. }
  537.  
  538. void dofib2a(int fib_num)
  539. {
  540.   start_timer ();
  541.   Integer result = fib2a(fib_num);
  542.   double time = return_elapsed_time(0.0);
  543.   cout << "fib2a = " << result << ". Time = " << time << "\n";
  544. }
  545.  
  546. void dofib2b(int fib_num)
  547. {
  548.   start_timer ();
  549.   Integer result = fib2b(fib_num);
  550.   double time = return_elapsed_time(0.0);
  551.   cout << "fib2b = " << result << ". Time = " << time << "\n";
  552. }
  553.  
  554. void dofib2c(int fib_num)
  555. {
  556.   start_timer ();
  557.   Integer result = fib2c(fib_num);
  558.   double time = return_elapsed_time(0.0);
  559.   cout << "fib2c = " << result << ". Time = " << time << "\n";
  560. }
  561.  
  562. void dofib3(int fib_num)
  563. {
  564.   start_timer ();
  565.   Memo_Fib    Memo_Test (fib_num);
  566.   Integer result = Memo_Test.fib3 (fib_num);
  567.   double time = return_elapsed_time(0.0);
  568.   cout << "fib3 = " << result << ". Time = " << time << "\n";
  569. }
  570.  
  571. void dofib3a(int fib_num)
  572. {
  573.   start_timer ();
  574.   Memo_Fib    Memo_Test (fib_num);
  575.   Integer result = Memo_Test.fib3a (fib_num);
  576.   double time = return_elapsed_time(0.0);
  577.   cout << "fib3a = " << result << ". Time = " << time << "\n";
  578. }
  579.  
  580. void dofib4(int fib_num)
  581. {
  582.   start_timer ();
  583.   Integer result = fib4 (fib_num);
  584.   double time = return_elapsed_time(0.0);
  585.   cout << "fib4 = " << result << ". Time = " << time << "\n";
  586. }
  587.  
  588. void dofib5(int fib_num)
  589. {
  590.   Closed_Form Rec_Test;
  591.   Integer result = Rec_Test.fib5 (fib_num);
  592.   double time = return_elapsed_time(0.0);
  593.   cout << "fib5 = " << result << ". Time = " << time << "\n";
  594. }
  595.  
  596.  
  597. int
  598. main (int argc, char *argv[])
  599. {
  600.   int         fib_num = (argc > 1) ? atoi (argv[1]) : DEFAULT_SIZE;
  601.  
  602.   cout << "Results for fib(" << fib_num << "):\n\n";
  603.  
  604.   dofib1(fib_num);
  605.   dofib2(fib_num);
  606.   dofib2a(fib_num);
  607.   dofib2b(fib_num);
  608.   dofib2c(fib_num);
  609.   dofib3(fib_num);
  610.   dofib3a(fib_num);
  611. //  dofib4(fib_num);  // This uses too much mem for large n!
  612.   dofib5(fib_num);
  613.   return 0;
  614. }
  615.  
  616.